const TOUCHPAD_BASE_HEIGHT = 300;
const TOUCHPAD_BASE_WIDTH = 400;
+const EVENT_HISTORY_THRESHOLD_MS = 150;
+
const SCROLL_MULTIPLIER = 10;
const SWIPE_MULTIPLIER = 0.5;
SCROLLING: 1,
};
+const EventHistory = class {
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ this._data = [];
+ }
+
+ trim(time) {
+ const thresholdTime = time - EVENT_HISTORY_THRESHOLD_MS;
+ const index = this._data.findIndex(r => r.time >= thresholdTime);
+
+ this._data.splice(0, index);
+ }
+
+ append(time, delta) {
+ this.trim(time);
+
+ this._data.push({ time, delta });
+ }
+
+ calculateVelocity() {
+ if (this._data.length < 2)
+ return 0;
+
+ const firstTime = this._data[0].time;
+ const lastTime = this._data[this._data.length - 1].time;
+
+ if (firstTime === lastTime)
+ return 0;
+
+ const totalDelta = this._data.slice(1).map(a => a.delta).reduce((a, b) => a + b);
+ const period = lastTime - firstTime;
+
+ return totalDelta / period;
+ }
+};
+
const TouchpadSwipeGesture = GObject.registerClass({
Properties: {
'enabled': GObject.ParamSpec.boolean(
this._allowedModes = allowedModes;
this._enabled = true;
this._distance = global.screen_height;
-
+ this._history = new EventHistory();
this._reset();
this._touchpadGesture = new TouchpadSwipeGesture(allowedModes);
this._prevOffset = 0;
this._progress = 0;
- this._prevTime = 0;
- this._velocity = 0;
-
this._cancelled = false;
+
+ this._history.reset();
}
_interrupt() {
if (this._state === State.SCROLLING)
return;
- this._prevTime = time;
+ this._history.append(time, 0);
let rect = new Meta.Rectangle({ x, y, width: 1, height: 1 });
let monitor = global.display.get_monitor_index_for_rect(rect);
delta = -delta;
this._progress += delta;
-
- if (time !== this._prevTime)
- this._velocity = delta / (time - this._prevTime);
+ this._history.append(time, delta);
let firstPoint = this._snapPoints[0];
let lastPoint = this._snapPoints[this._snapPoints.length - 1];
this._initialProgress - 1, this._initialProgress + 1);
this.emit('update', this._progress);
-
- this._prevTime = time;
}
_getClosestSnapPoints() {
return [lower, upper];
}
- _getEndProgress() {
+ _getEndProgress(velocity) {
if (this._cancelled)
return this._cancelProgress;
let middle = (upper + lower) / 2;
if (this._progress > middle) {
- let thresholdMet = this._velocity * this._distance > -VELOCITY_THRESHOLD;
+ const thresholdMet = velocity * this._distance > -VELOCITY_THRESHOLD;
return thresholdMet || this._initialProgress > upper ? upper : lower;
} else {
- let thresholdMet = this._velocity * this._distance < VELOCITY_THRESHOLD;
+ const thresholdMet = velocity * this._distance < VELOCITY_THRESHOLD;
return thresholdMet || this._initialProgress < lower ? lower : upper;
}
}
- _endGesture(_gesture, _time) {
+ _endGesture(_gesture, time) {
if (this._state !== State.SCROLLING)
return;
return;
}
- let endProgress = this._getEndProgress();
+ this._history.trim(time);
+
+ let velocity = this._history.calculateVelocity();
+ const endProgress = this._getEndProgress(velocity);
- let velocity = ANIMATION_BASE_VELOCITY;
- if ((endProgress - this._progress) * this._velocity > 0)
- velocity = this._velocity;
+ if ((endProgress - this._progress) * velocity <= 0)
+ velocity = ANIMATION_BASE_VELOCITY;
let duration = Math.abs((this._progress - endProgress) / velocity * DURATION_MULTIPLIER);
if (duration > 0) {
this._progress = currentProgress;
this._cancelProgress = cancelProgress;
- this._velocity = 0;
this._state = State.SCROLLING;
}